const debug = require('../util/debug').makeFileLogger(__filename);
const wordwrap = require('word-wrap');
const loadEnvironment = require('./loadEnvironment');
const prettyLogger = require('../util/pretty-logger');
const getEnvVarDefinitions = require('./getEnvVarDefinitions');

const buildpackVersion = require('../../package.json').version;
const LINE_PREFIX = '#   ';
const MAX_WIDTH = 80;

const blankline = '#\n';
const hashes = length => Array.from({ length }, () => '#').join('');
const endSection = hashes(MAX_WIDTH) + '\n';
const startSection = (label, offset) => {
    const start = `${hashes(offset)} ${label} `;
    return start + hashes(MAX_WIDTH - start.length) + '\n';
};

const graf = txt =>
    wordwrap(txt, {
        indent: LINE_PREFIX,
        width: MAX_WIDTH - LINE_PREFIX.length - 1
    }) + '\n';
const paragraphs = (...grafs) => grafs.map(graf).join(blankline);

module.exports = async function printEnvFile(dirOrEnv, options = {}) {
    const { logger = prettyLogger, useExamples } = options;
    // All environment variables Buildpack and PWA Studio use should be defined
    // in envVarDefinitions.json, along with recent changes to those vars for
    // logging.
    const context = typeof dirOrEnv === 'string' ? dirOrEnv : process.cwd();

    if (context !== dirOrEnv) {
        logger.warn(
            `Passing an env object instead of a project root path to createEnvFile is deprecated. This run will assume that process.cwd(), which is "${context}", is your project root.`
        );
    }

    const definitions = getEnvVarDefinitions(context);
    const { env, error } = await loadEnvironment(dirOrEnv, logger, definitions);
    if (error && !useExamples) {
        logger.warn(
            `The current environment is not yet valid; please set any missing variables to build the project before generating a .env file.`
        );
    }
    let contents = startSection('PWA Studio Environment Variables', 8);
    contents += blankline;
    contents += paragraphs(
        'This file contains environment variables for a Magento PWA Studio project. PWA Studio uses environment variables for all variable cross-project values, so that a developer or a build system can override any variable with standard tools.',
        'This file belongs at the root of the PWA, and must be named `.env`. Uncomment and modify variable declarations in this file and they will take effect throughout the Buildpack tool chain.',
        `Generated by @magento/pwa-buildpack v${buildpackVersion} on ${new Date().toISOString()}.`
    );
    contents += blankline + endSection;
    for (const section of definitions.sections) {
        contents += '\n' + startSection(section.name, 4) + blankline;
        for (const variable of section.variables) {
            debug('section "%s" variable %s', section.name, variable.name);
            const hasDefault = variable.hasOwnProperty('default');
            const setAlready = env.hasOwnProperty(variable.name);
            const defaultOverridden =
                hasDefault &&
                setAlready &&
                env[variable.name] !== variable.default;
            const shouldSetFromEnv = defaultOverridden || setAlready;
            const shouldSetExample =
                useExamples && variable.hasOwnProperty('example');
            const isSet = shouldSetFromEnv || shouldSetExample;
            let currentValue = '';
            if (shouldSetFromEnv) {
                currentValue = env[variable.name];
            } else if (shouldSetExample) {
                currentValue = variable.example;
            }

            contents += graf(variable.desc);

            if (!isSet && variable.example) {
                contents += graf(`- Example: ${variable.example}`);
            }
            if (hasDefault) {
                contents += graf(`- Default when not set: ${variable.default}`);
            }
            if (isSet) {
                contents += `${variable.name}=${currentValue}\n`;
            } else {
                // Print this line as an example of how to set the variable, but
                // comment it out so that future versions can inherit changed
                // default settings.
                contents += `#${variable.name}=${variable.default || ''}\n`;
            }
            contents += blankline;
        }
        contents += endSection;
    }
    return contents;
};
